#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>

#include "err.hpp"

using namespace std;

static void usage(string s)
{
    cout << "Usage\n\t"
         << " " << s << "port\n"
         << endl;
}
// 肯定是知道sever的ip和port然后才发信息
//  ./tcp_client severip severport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        usage(argv[0]);
        exit(USAGE_ERR);
    }
    string severip = argv[1];
    uint16_t severport = atoi(argv[2]);
    // 1.创建套接字
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        std::cerr << "create error" << strerror(errno) << std::endl;
        exit(SOCKET_ERR);
    }
    // 客户端需要bind 不需要自己bind（交给oS)
    // 不需要监听和accept——都是服务器做的
    // 2.connect
    struct sockaddr_in sever;
    memset(&sever, 0, sizeof(sever));
    socklen_t len = sizeof(sever);
    sever.sin_port = htons(severport);
    inet_aton(severip.c_str(), &sever.sin_addr); // 把点分十进制字符串转换成四字节整数,输出型参数 点的优先级高

    sever.sin_family = AF_INET;
    int cnt = 5;
    while (connect(sock, (struct sockaddr *)&sever, len) != 0) // connect and bind
    {
        sleep(1);
        cout << "连接失败 尝试重连" << cnt-- << endl;
        if (cnt <= 0)
            break;
    }
    if (cnt <= 0)
    {
        std::cerr << "connect error" << strerror(errno) << std::endl;
        exit(CONNECT_ERR);
    }
    // 3.连接成功
    while (true)
    {
        string line;
        cout << "Please Enter>>>";
        getline(cin, line);

        // 把输入的字符串给服务器传过去
        write(sock, line.c_str(), line.size());

        // 读出来
        char buffer[1024];
        ssize_t n = read(sock, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = '\0';
            cout << "sever echo>>>" << buffer << endl;
        }
        else if (n == 0)
        {
            // 说明服务器的挂了
            std::cout << "sever quit" << std::endl;
            break;
        }
        else
        {
            std::cerr << "read error" << strerror(errno) << std::endl;
            exit(READ_ERR);
        }
    }
    close(sock); // 文件描述符的生命周期随进程
    return 0;
}